SOK-2302 Seminar - klima

Author

Andrea Mannberg

Introduksjon

I denne oppgaven skal vi se på data knyttet til klimaendringer. Datamaterialet kommer i fra National Oceanic and Atmospheric Administration (NOAA), som er USA:s tilsvarighet til meteorologisk institutt. NOAA har beregnet avvik fra “normalen” (gjenomsnittet i tidsperioden 1970 - 2000) for hvert år og måned i perioden 1850 - 2025. Dere finner datamaterialet her. Dere finner informasjon om innholdet i de ulike filene i ReadMe-filen.

Navnen på datafilene gir informasjon om hva filene innholder. For eksempel innholder filen “aravg.ann.land.00N.30N.v6.0.0.202508.asc” årlig informasjon for avvik i temperaturen over land (landoverflaten) fra ekvatorn (00N) til den 30. breddegraden. Filen inneholder data fram til august i 2025. Filen “aravg.ann.ocean.00N.30N.v6.0.0.202508.asc” har samme informasjon for temperaturen over havet, og “aravg.ann.land_ocean.00N.30N.v6.0.0.202508.asc” inneholder informasjon om totale temperaturavvik (ved overflaten).

Lattituder: * 90S-90N: Hele jorden * 00N - 90N: Fra ekvatorn til nordpolen (nordlig hemisfere) * 90S-00N: Fra sydpolen til ekvatorn (sydlig hemisfere) * 20S - 20N: Tropikene * 60N - 90N: Arktis * 90S - 60S: Antarktis

Når denne oppgaven ble laget, var den amerikanske staten stengt og hjemmesiden ser derfor litt rar ut. Det går likevel å hente data fra siden.

Det er fritt fram å bruke KI til å løse oppgavene, men se til at du har en forståelse for koden som KI lager. Det er ditt ansvar å se til at koden er riktig!

Oppgave 1 - Enkel grafisk illustrasjon av globale temperaturavvik i perioden 1850 - 2025

I denne oppgaven skal vi se på hvordan avviket i temperatur i havet og på land har varierer globalt sett fra år til år.

  • Hent data for globale månedlige temperaturavvik for hav og land
  • Lag en ny data frame med år, måned, og temperaturavvik
  • Beregn gjennomsnittligt måntlig avvik i temperaturen og lag en ny variabel som inneholder denne informasjonen.
  • Lag en graf som viser avvik fra normalen (Temperature anomaly) for hvert år i tidsperioden 1850-2025. Y-akselen skal vise avvik, X-akselen skal vise måneder. Bruk en linje-graf der enhvert år har en egen linje.
  • Legg in svart, litt tykkere linje som viser gjennomsnittlig avvik fra normalen i perioden.
library(tidyverse)
library(ggplot2)
library(scales)
  • Hva kan du utlese fra denne konklusjonen om global oppvarming?

Oppgave 2 - En litt mer lesbar graf

Som følge av det store tidsrommet er det vanskelig å få et bilde av utvikling over tid i den enkle grafen. I denne oppgaven skal vi derfor prøve å gjøre ting litt mere oversiktlig.

  • Lag en graf der alle år fram til og med 2015 har en grå farge og er litt tynnere, mens temperaturavvikene i perioden 2015-2025 har ulike farger. Behold legenden for årene 2015 - 2025, fjern legenden for årene før.
  • Hvilke konklusjoner kan vi trekke fra denne grafen i henhold til global oppvarming?

Oppgave 3 - Trender over tid i ulike regioner

I denne oppgaven skal vi se på temperaturavvik i ulike regioner i verden. Med regioner avses her ulike lattitude-bånd på jorden: Hele jorden, nordlige halvkulen, sørlige halvkulen, tropikkene, arktis, og antarktis. For å kunne sammenligne utviklingen i de ulike regionene bruker vi gjennomsnittlige avvik. Mer presist bruker vi et rullende 5-års gjennomsnitt på avvikene. Dataene hentes akkurat som før fra NOAA, men siden vi ønsker å sammenligne ulike regioner må vi bruke flere datafiler. I oppgaven under ser vi på overflate-temperaturen i havet og på land tatt sammen. Du kan selv prøve å endre koden slik at du ser på hav og land separat.

  • Kjør koden
  • Hvordan skal vi tolke innholdet i grafen?
# Packer som trengs
library(tidyverse)
library(zoo)    # for å beregne rullende gjennomsnitt
library(lubridate)

# ---- PARAMETRER ----
# Version / date tag i NOAA fil-navn. bytt ut hvis NOAA har nyere filer.
version_tag <- "v6.0.0.202508"

# Variabel som skal brukes (valg av data-fil): "land_ocean", "land", or "ocean"
varname <- "land_ocean"

# Latittude bånd som skal sammenlignes (southLimit.northLimit)
bands <- c(
  "90S.90N",   # Globalt
  "00N.90N",   # Den nordlige halvkule
  "90S.00N",   # Den sørlige halvkule
  "60N.90N",   # Arktis
  "90S.60S",   # Antarktis
  "20S.20N"    # Tropikene
)

# Output
base_url <- "https://www.ncei.noaa.gov/data/noaa-global-surface-temperature/v6/access/timeseries"

# Rullende vindu for smoothing (år)
smooth_yrs <- 5


# ---- FUNKTIONER ----
read_noaa_aravg <- function(band, var = varname, version = version_tag) {
  # Lag filenavn og url
  fname <- sprintf("aravg.mon.%s.%s.%s.asc", var, band, version)
  url <- paste(base_url, fname, sep = "/")
  
  # les tabell (NOAA ascii: first cols year, month, anomaly, ...; comment lines start with #)
  df <- read.table(url, header = FALSE, comment.char = "#", stringsAsFactors = FALSE)
  colnames(df)[1:3] <- c("year", "month", "anomaly")
  df <- df %>%
    select(year, month, anomaly) %>%
    mutate(year = as.integer(year),
           month = as.integer(month),
           anomaly = as.numeric(anomaly),
           band = band)
  return(df)
}

# ---- LAST INN DATA OG KOMBINER DATAKILDER ----
all_monthly <- map_df(bands, ~ read_noaa_aravg(.x))

# ---- Avvik er i Kelvin (K). Dette er lik °C for forskjeller. Vi behåndler dem som °C ----

# ---- ÅRLIGE GJENNOMSNITT ----
annual <- all_monthly %>%
  group_by(band, year) %>%
  summarise(
    n_months = sum(!is.na(anomaly)),
    annual_anom = ifelse(n_months >= 10, mean(anomaly, na.rm = TRUE), NA_real_),
    .groups = "drop"
  ) %>%
  filter(!is.na(annual_anom))

# ---- BASENIVÅ (1971-2000)  ----
# Månedlig basenivå per måned og region
baseline_monthly <- all_monthly %>%
  filter(year >= 1971, year <= 2000) %>%
  group_by(band, month) %>%
  summarise(clim = mean(anomaly, na.rm = TRUE), .groups = "drop")

# Årlig basenivå (gjennomsnitt av 12 månedlige klimaverdier) per region:
baseline_yr_mean <- baseline_monthly %>%
  group_by(band) %>%
  summarise(baseline_mean = mean(clim, na.rm = TRUE), .groups = "drop")

# Substraher årlig gjennomsnittlig for basenivået (1971-2000) slik at avvikene er eksplisitt relativt til dette basenivået:
annual <- annual %>%
  left_join(baseline_yr_mean, by = "band") %>%
  mutate(annual_anom_clim = annual_anom - baseline_mean) # Dette skal være approksimativt lik orginale anomalier i datamaterialet men sikkerstiller et eksplisitt basenivå

# ---- SMOOTHING (rullende gjennomsnitt) ----
# Konverter til tidsserie og beregne rullende gjennomsnitt (år)
annual <- annual %>%
  arrange(band, year) %>%
  group_by(band) %>%
  mutate(
    roll_n = rollapply(annual_anom_clim, width = smooth_yrs, FUN = mean, align = "center", fill = NA, na.rm = TRUE),
    decade = year / 10
  ) %>%
  ungroup()
library(ggplot2)

# Palette for regions
region_colors <- c(
  "Globalt" = "black",
  "Nordlige halvkulen" = "#8856a7",
  "Sørlige halvkulen" = "#7fcdbb",
  "Arktis" = "red", 
  "Antarktis" = "#2c7fb8",
  "Tropikkene" = "#fed976"
)
# Mappe region-koder til fine labels
band_labels <- tribble(
  ~band,       ~label,
  "90S.90N",   "Globalt",
  "00N.90N",   "Nordlige halvkulen",
  "90S.00N",   "Sørlige halvkulen",
  "60N.90N",   "Arktis",
  "90S.60S",   "Antarktis",
  "20S.20N",   "Tropikkene"
)

plot_df <- annual %>% left_join(band_labels, by = "band")



# Graf
p <- ggplot(plot_df, aes(x = year)) +
  geom_line(aes(y = annual_anom_clim, group = band), color = "grey70", alpha = 0.4) +
  geom_line(aes(y = roll_n, color = label), size = 1.1, na.rm = TRUE) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "black") +
  labs(
    title = "Årlige temperaturavvik (relativt til 1971–2000)",
    subtitle = paste0(smooth_yrs, "-års løpende gjennomsnitt; avikk i K (ekvivalent til °C for forskjeller)"),
    x = "År",
    y = "Temperaturavvik(°C)",
    color = "Region"
  ) +
  scale_x_continuous(breaks = seq(1850, 2030, by = 20)) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom")


my_colors <- c("black","#8856a7", "#7fcdbb", "red", "#2c7fb8", "#fed976")
names(my_colors) <- band_labels$label
p <- p + scale_color_manual(values = my_colors)

Interaktiv plot

library(dplyr)
library(ggplot2)
library(plotly)
library(htmlwidgets)

# Beregne sikkre begrensninger for 7 (5% buffer) (ikke sikkert at dette er strikt nødvendig)
ymin <- min(plot_df$annual_anom_clim, plot_df$roll_n, na.rm = TRUE)
ymax <- max(plot_df$annual_anom_clim, plot_df$roll_n, na.rm = TRUE)
pad <- 0.05 * (ymax - ymin + 1e-9)
ymin <- ymin - pad; ymax <- ymax + pad

# Bestem rekkefølgen for de ulike regionene
region_order <- c("60N.90N","00N.90N","20S.20N","90S.00N","90S.60S", "90S.90N")
plot_df <- plot_df %>%
 mutate(band = factor(band, levels = region_order))

# Lag en "desired label order" som tilsvarer region_order
desired_label_order <- band_labels$label[match(region_order, band_labels$band)]

plot_df <- plot_df %>%
  mutate(label = factor(label, levels = desired_label_order)) %>%
  # arrange so ggplot draws traces in the same order as the factor levels
  arrange(label, year)

# Bestem farger
my_colors <- c("red", "#8856a7", "#fed976", "#7fcdbb","#2c7fb8",  "black")
names(my_colors) <- desired_label_order

p <- ggplot(plot_df, aes(x = year)) +
  # background thin lines: group by label so each region becomes one trace
  geom_line(aes(y = annual_anom_clim, group = label),
            color = "grey70", alpha = 0.4) +
  # smoothed lines: group & color by label; put tooltip inside aes()
  geom_line(aes(y = roll_n, color = label, group = label,
                text = paste0(
                  "Region: ", label, "<br>",
                  "År: ", year, "<br>",
                  "Avvik: ", ifelse(is.na(roll_n), "NA",
                                             paste0(round(roll_n, 3), " °C"))
                )),
            size = 1.1, na.rm = TRUE) +
  geom_hline(yintercept = 0, linetype = "dashed") +
  labs(title = "Årlige temperaturavvik (relativt til 1971–2000)",
       subtitle = paste0(smooth_yrs, "-års løpende gjennomsnitt; avvik i K (ekvivalent til °C)"),
       x = "År", y = "Temperaturavvik (°C)", color = "Region") +
  scale_x_continuous(breaks = seq(1850, 2030, by = 20)) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom")



p <- p + scale_color_manual(values = my_colors)

# convert to plotly, use only the text tooltip, and set y axis range explicitly
gg_region <- ggplotly(p, tooltip = "text") %>%
  layout(hovermode = "x unified",
         #height = 16 * 250,
         yaxis = list(range = c(ymin, ymax))) %>%
  config(displayModeBar = TRUE)



# JavaScript: add/update vertical line at clicked x
js <- "
function(el, x) {
  var plot = el;
  plot.on('plotly_click', function(data) {
    if(!data || !data.points || data.points.length === 0) return;
    var xval = data.points[0].x;
    var vline = {
      type: 'line',
      x0: xval, x1: xval,
      y0: 0, y1: 1,
      xref: 'x', yref: 'paper',
      line: {color: 'black', width: 1.5, dash: 'dot'}
    };
    var shapes = (plot.layout.shapes || []).filter(function(s){ return s.name !== 'click_vline'; });
    vline.name = 'click_vline';
    shapes.push(vline);
    Plotly.relayout(plot, {'shapes': shapes});
  });
}
"

gg_region <- onRender(gg_region, js)

gg_region

Oppgave 4

  • Gjør om oppgave 3, men sammenlign temperatur for hav og land (globalt)
# Packer som trengs
library(tidyverse)
library(zoo)    # for å beregne rullende gjennomsnitt
library(lubridate)

# ---- PARAMETRER ----
# Version / date tag i NOAA fil-navn. bytt ut hvis NOAA har nyere filer.
version_tag <- "v6.0.0.202508"

# Variabel som skal brukes (valg av data-fil): "land_ocean", "land", or "ocean"
varname <- "90S.90N"

# Latittude bånd som skal sammenlignes (southLimit.northLimit)
type <- c(
  "land",   # land
  "ocean"   # hav
)

# Output
base_url <- "https://www.ncei.noaa.gov/data/noaa-global-surface-temperature/v6/access/timeseries"

# Rullende vindu for smoothing (år)
smooth_yrs <- 5


# ---- FUNKTIONER ----
read_noaa_aravg <- function(type, var = varname, version = version_tag) {
  # Lag filenavn og url
  fname <- sprintf("aravg.mon.%s.%s.%s.asc", type, var, version)
  url <- paste(base_url, fname, sep = "/")
  
  # les tabell (NOAA ascii: first cols year, month, anomaly, ...; comment lines start with #)
  df <- read.table(url, header = FALSE, comment.char = "#", stringsAsFactors = FALSE)
  colnames(df)[1:3] <- c("year", "month", "anomaly")
  df <- df %>%
    select(year, month, anomaly) %>%
    mutate(year = as.integer(year),
           month = as.integer(month),
           anomaly = as.numeric(anomaly),
           type = type)
  return(df)
}

# ---- LAST INN DATA OG KOMBINER DATAKILDER ----
all_monthly <- map_df(type, ~ read_noaa_aravg(.x))

# ---- ÅRLIGE GJENNOMSNITT ----
annual <- all_monthly %>%
  group_by(type, year) %>%
  summarise(
    n_months = sum(!is.na(anomaly)),
    annual_anom = ifelse(n_months >= 10, mean(anomaly, na.rm = TRUE), NA_real_),
    .groups = "drop"
  ) %>%
  filter(!is.na(annual_anom))

# ---- BASENIVÅ (1971-2000)  ----
# Månedlig basenivå per måned og region
baseline_monthly <- all_monthly %>%
  filter(year >= 1971, year <= 2000) %>%
  group_by(type, month) %>%
  summarise(clim = mean(anomaly, na.rm = TRUE), .groups = "drop")

# Årlig basenivå (gjennomsnitt av 12 månedlige klimaverdier) per region:
baseline_yr_mean <- baseline_monthly %>%
  group_by(type) %>%
  summarise(baseline_mean = mean(clim, na.rm = TRUE), .groups = "drop")

# Substraher årlig gjennomsnittlig for basenivået (1971-2000) slik at avvikene er eksplisitt relativt til dette basenivået:
annual <- annual %>%
  left_join(baseline_yr_mean, by = "type") %>%
  mutate(annual_anom_clim = annual_anom - baseline_mean) # Dette skal være approksimativt lik orginale anomalier i datamaterialet men sikkerstiller et eksplisitt basenivå

# ---- SMOOTHING (rullende gjennomsnitt) ----
# Konverter til tidsserie og beregne rullende gjennomsnitt (år)
annual <- annual %>%
  arrange(type, year) %>%
  group_by(type) %>%
  mutate(
    roll_n = rollapply(annual_anom_clim, width = smooth_yrs, FUN = mean, align = "center", fill = NA, na.rm = TRUE),
    decade = year / 10
  ) %>%
  ungroup()
library(ggplot2)

# Palette for regions
type_colors <- c(
  "Hav" = "#2c7fb8",
  "Land" = "#7fcdbb"
)
# Mappe region-koder til fine labels
type_labels <- tribble(
  ~type,       ~label,
  "ocean",      "Hav",
  "land",       "Land"
)

plot_df <- annual %>% left_join(type_labels, by = "type")

# Graf
p_type <- ggplot(plot_df, aes(x = year)) +
  geom_line(aes(y = annual_anom_clim, group = type), color = "grey70", alpha = 0.4) +
  geom_line(aes(y = roll_n, color = label), size = 1.1, na.rm = TRUE) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "black") +
  labs(
    title = "Årlige temperaturavvik (relativt til 1971–2000)",
    subtitle = paste0(smooth_yrs, "-års løpende gjennomsnitt; avikk i K (ekvivalent til °C for forskjeller)"),
    x = "År",
    y = "Temperaturavvik(°C)",
    color = "Type"
  ) +
  scale_x_continuous(breaks = seq(1850, 2030, by = 20)) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom")

my_colors <- c("#2c7fb8", "#7fcdbb")
names(my_colors) <- type_labels$label

p_type <- p_type + scale_color_manual(values = my_colors)

print(p_type)